﻿/* *
 * =============================================================================
 * File Name:               Switch.cs
 * Programmer:              Ken Watts
 * Creation Date:           8/3/2008
 * =============================================================================
 * Description:             Class that represents a switch.
 * =============================================================================
 * Modification Date:       8/3/2008 - Initial Revision
 * =============================================================================
 * */

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DigitalLogicSimulator
{
    public class Switch : Device
    {
        // Constants
        private const int k_int_num_inputs = 1;
        private const int k_int_input_x_offset = 2;
        private const int k_int_input_y_offset = 1;
        private const int k_int_input = 0;
        private const double k_initial_top = 145;
        private const double k_initial_left = 15;
        // Perimeter points
        private const double k_perim_p1_x = 10;
        private const double k_perim_p1_y = 0;
        private const double k_perim_p2_x = 22;
        private const double k_perim_p2_y = 0;
        private const double k_perim_p3_x = 22;
        private const double k_perim_p3_y = 20;
        private const double k_perim_p4_x = 10;
        private const double k_perim_p4_y = 20;
        // Switch points
        private const double k_p1_x = 10;
        private const double k_p1_y = 12.5;
        private const double k_p2_x_open = 17;
        private const double k_p2_y_open = 2;
        private const double k_p2_x_closed = 22;
        private const double k_p2_y_closed = 12.5;
        // Inputs and outputs
        private const double k_in_p1_x = 0;
        private const double k_in_p1_y = 12.5;
        private const double k_in_p2_x = 10;
        private const double k_in_p2_y = 12.5;
        private const double k_out_p1_x = 22;
        private const double k_out_p1_y = 12.5;
        private const double k_out_p2_x = 32;
        private const double k_out_p2_y = 12.5;

        // Input states
        bool inputState = false, b_open=true;
        protected PathGeometry pathGeometryClosed;

        public Switch()
        {
            // Create an array for the input devices
            inputDevices = new Device[k_int_num_inputs];
            // Initialize the output state
            outputState = false;
        }

        protected override void ReplaceInstance(Panel parentPanel)
        {
            parentPanel.Children.Add(new Switch());
        }

        protected override void Draw()
        {
            pathGeometry = new PathGeometry();
            pathGeometryClosed = new PathGeometry();
            pathGeometry.FillRule = FillRule.Nonzero;
            pathGeometryClosed.FillRule = FillRule.Nonzero;
            PathFigure pathFigure = new PathFigure();
            PathFigure pathFigureClosed = new PathFigure();
            pathFigure.StartPoint = new Point(k_p1_x, k_p1_y);
            pathFigureClosed.StartPoint = new Point(k_p1_x, k_p1_y);
            pathGeometry.Figures = new PathFigureCollection();
            pathGeometryClosed.Figures = new PathFigureCollection();
            pathGeometry.Figures.Add(pathFigure);
            pathGeometryClosed.Figures.Add(pathFigureClosed);
            pathFigure.Segments = new PathSegmentCollection();
            pathFigureClosed.Segments = new PathSegmentCollection();

            // Draw switch 
            LineSegment ls = new LineSegment();
            ls.Point = new Point(k_p2_x_open, k_p2_y_open);
            pathFigure.Segments.Add(ls);
            ls = new LineSegment();
            ls.Point = new Point(k_p2_x_closed, k_p2_y_closed);
            pathFigureClosed.Segments.Add(ls);

            // Draw perimeter - open
            pathFigure = new PathFigure();
            pathFigure.IsClosed = true;
            pathFigure.StartPoint = new Point(k_perim_p1_x, k_perim_p1_y);
            pathGeometry.Figures.Add(pathFigure);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p2_x, k_perim_p2_y);
            pathFigure.Segments.Add(ls);
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p3_x, k_perim_p3_y);
            pathFigure.Segments.Add(ls);
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p4_x, k_perim_p4_y);
            pathFigure.Segments.Add(ls);
            // Draw perimeter - closed
            pathFigure = new PathFigure();
            pathFigure.IsClosed = true;
            pathFigure.StartPoint = new Point(k_perim_p1_x, k_perim_p1_y);
            pathGeometryClosed.Figures.Add(pathFigure);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p2_x, k_perim_p2_y);
            pathFigure.Segments.Add(ls);
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p3_x, k_perim_p3_y);
            pathFigure.Segments.Add(ls);
            ls = new LineSegment();
            ls.Point = new Point(k_perim_p4_x, k_perim_p4_y);
            pathFigure.Segments.Add(ls);

            // Add Input & output
            // Input
            pathFigure = new PathFigure();
            pathFigure.StartPoint = new Point(k_in_p1_x, k_in_p1_y);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_in_p2_x, k_in_p2_y);
            pathFigure.Segments.Add(ls);
            pathGeometry.Figures.Add(pathFigure);
            pathFigure = new PathFigure();
            pathFigure.StartPoint = new Point(k_in_p1_x, k_in_p1_y);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_in_p2_x, k_in_p2_y);
            pathFigure.Segments.Add(ls);
            pathGeometryClosed.Figures.Add(pathFigure);
            // Output 
            pathFigure = new PathFigure();
            pathFigure.StartPoint = new Point(k_out_p1_x, k_out_p1_y);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_out_p2_x, k_out_p2_y);
            pathFigure.Segments.Add(ls);
            pathGeometry.Figures.Add(pathFigure);
            pathFigure = new PathFigure();
            pathFigure.StartPoint = new Point(k_out_p1_x, k_out_p1_y);
            pathFigure.Segments = new PathSegmentCollection();
            ls = new LineSegment();
            ls.Point = new Point(k_out_p2_x, k_out_p2_y);
            pathFigure.Segments.Add(ls);
            pathGeometryClosed.Figures.Add(pathFigure);

            // Setup the path
            path = new Path();
            path.Stretch = Stretch.Fill;
            path.StrokeLineJoin = PenLineJoin.Round;
            path.Stroke = new SolidColorBrush(strokeColor);
            path.Fill = new SolidColorBrush(backgroundColor);
            path.StrokeThickness = k_dbl_stroke_thickness;
            path.Data = pathGeometry;
            path.MouseLeftButtonDown += new MouseButtonEventHandler(CustomMouseLeftButtonDown);
            path.MouseLeftButtonUp += new MouseButtonEventHandler(CustomMouseLeftButtonUp);
            path.MouseMove += new MouseEventHandler(CustomMouseMove);
            path.MouseEnter += new MouseEventHandler(CustomMouseEnter);
            path.MouseLeave += new MouseEventHandler(CustomMouseLeave);

            Children.Add(path);

            // Setup output points
            outputLeft.X = k_out_p1_x;
            outputLeft.Y = k_out_p1_y;
            outputRight.X = k_out_p2_x;
            outputRight.Y = k_out_p2_y;

            // Setup rubberband output line
            outputLine = new Line();
            outputLine.Stroke = new SolidColorBrush(rubberbandColor);
            outputLine.StrokeThickness = k_dbl_stroke_thickness;
            outputLine.Visibility = System.Windows.Visibility.Collapsed;

            Children.Add(outputLine);

            // Set the initial position
            path.SetValue(Canvas.LeftProperty, k_initial_left);
            path.SetValue(Canvas.TopProperty, k_initial_top);
            canvLeft = k_initial_left;
            canvTop = k_initial_top;
        }

        protected override int Connect(Device logicDevice, Point clickedPoint)
        {
            // Inverter has only 1 input
            if (inputDevices[k_int_input] == null)
            {
                inputDevices[k_int_input] = logicDevice;
                return k_int_input;
            }
            // Can't connect...
            return k_int_not_connected;
        }

        protected override Point GetInputLocation(int inputNumber)
        {
            Point p = new Point();
            // Figure out the correct point information
            if (inputNumber == k_int_input)
            {
                p.X = canvLeft + k_in_p1_x + k_int_input_x_offset;
                p.Y = canvTop + k_in_p1_y + k_int_input_y_offset;
            }
            return p;
        }

        // Returns the output location of the device
        public override Point GetOutputLocation()
        {
            return new Point(outputRight.X + canvLeft, 
                outputRight.Y + canvTop + 1);
        }

        // Sets the state of the specified input to the given value
        public override void SetInputState(int inputNumber, bool state)
        {
            if (inputNumber == k_int_input)
                // Set the first input state
                inputState = state;
            // Set the output state - Prevent infinite feedback
            bool newOutputState;
            if (!b_open)
                newOutputState = inputState;
            else
                newOutputState = false;
            if (newOutputState != outputState)
            {
                outputState = newOutputState;
                // Set the input state of any connected devices
                UpdateOutputState();
            }
        }

        protected override void CustomMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // Determine whether or not the switch should be re-drawn
            double x_click = e.GetPosition(this).X;
            FrameworkElement fe = sender as FrameworkElement;
            double switch_left = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));

            if (x_click > switch_left + k_in_p2_x && x_click < switch_left + k_out_p1_x && initiated && !moving)
            {
                b_open = !b_open;
                if (b_open)
                {
                    path.Data = pathGeometry;
                }
                else
                {
                    path.Data = pathGeometryClosed;
                }
                SetInputState(k_int_input, inputState);
            }
            // Call the superclass method
            base.CustomMouseLeftButtonUp(sender, e);
        }
    }
}
